package org.bdware.doip.core.codec.packet;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.handler.codec.http.websocketx.*;
import org.apache.log4j.Logger;
import org.bdware.doip.core.doipMessage.DoipMessage;
import org.bdware.doip.core.doipMessage.MessageEnvelope;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class PacketNettyWSCodec extends MessageToMessageCodec<WebSocketFrame, DoipMessage> {

    Logger logger = Logger.getLogger(PacketNettyWSCodec.class);
    PacketMessageCodec codec = new PacketMessageCodecImpl();
    HashMap<Integer,AbstractEnvelopeReceiveBuffer> receiveBufferMap = new HashMap<>();
    @Override
    protected void encode(ChannelHandlerContext ctx, DoipMessage msg, List<Object> out) throws Exception {
        logger.debug("encoding DOIP message to byteBuffer,msg body length: " + msg.body.getEncodedData().length);
        if (msg.getMTU() < Integer.MAX_VALUE) {
            logger.info("MTU suggested to be max,change MTU to Integer.MAX_VALUE");
            msg.setMTU(Integer.MAX_VALUE);
        }
        ByteBuf buff = Unpooled.directBuffer();
        ArrayList<MessageEnvelope> envelpoes = codec.MessageToEnvelopes(msg);
        for(MessageEnvelope env:envelpoes){
            buff.writeBytes(codec.EnvelopeToBytes(env));
        }
        logger.debug("finish encoding, length: " + buff.readableBytes());
        BinaryWebSocketFrame frame = new BinaryWebSocketFrame(buff);
        out.add(frame);
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, List<Object> out) throws Exception {
        logger.debug("decoding frame to DOIP message");
        if(!(frame instanceof BinaryWebSocketFrame)){
            logger.debug("only support binary ws frame");
            return;
        }
        ByteBuf msg = frame.content();
        byte[] envBytes = new byte[msg.readableBytes()];
        msg.readBytes(envBytes);
        MessageEnvelope envelope = codec.BytesToEnvelope(envBytes);
        AbstractEnvelopeReceiveBuffer recvBuffer = receiveBufferMap.get(envelope.requestId);
        if(recvBuffer == null){
            recvBuffer = new NoResendEnvelopeReceiveBuffer(envelope.requestId,ctx);
            receiveBufferMap.put(envelope.requestId,recvBuffer);
        }
        recvBuffer.addEnvelope(envelope);
        if(recvBuffer.isCompleted()){
            ArrayList<MessageEnvelope> envList = recvBuffer.getSortedEnvelopes();
            out.add(codec.EnvelopesToMessage(envList));
            receiveBufferMap.remove(envelope.requestId);
        }
    }
}
